package com.senlingspace.system.event;

import com.alibaba.fastjson2.JSON;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Sets;
import com.senlingspace.common.core.dingtalk.DingTalkService;
import com.senlingspace.common.enums.OrderType;
import com.senlingspace.common.utils.DateUtils;
import com.senlingspace.common.utils.MoneyUtils;
import com.senlingspace.system.domain.*;
import com.senlingspace.system.domain.dto.RefundOrderDto;
import com.senlingspace.system.domain.vo.SharesInvestorVo;
import com.senlingspace.system.mapper.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 事件处理逻辑
 *
 * @author lebronjames
 * @date 2024/08/05
 */
@Slf4j
@Component
@AllArgsConstructor
public class EventHandlerService {

    private final SlkjFareSchemeDetailMapper slkjFareSchemeDetailMapper;
    private final SlkjOrderSplitSnapshotMapper slkjOrderSplitSnapshotMapper;
    private final SlkjShopInvestorMapper slkjShopInvestorMapper;
    private final SlkjShopOperateMapper slkjShopOperateMapper;
    private final SlkjOrderPricingSnapshotMapper slkjOrderPricingSnapshotMapper;
    private final SlkjTicketCategoryMapper slkjTicketCategoryMapper;
    private final SlkjOrderMapper slkjOrderMapper;
    private final DingTalkService dingTalkService;
    private final SlkjOrderTrackingMapper slkjOrderTrackingMapper;
    private final SlkjFareSchemeMapper slkjFareSchemeMapper;


    @Transactional
    public void handleTicketCategory(SlkjTicketCategory slkjTicketCategory) {
        List<SlkjFareSchemeDetail> fareSchemeDetailList = new ArrayList<>();
        try {
            log.info("事件 ID：{}", "slkj_ticket_category#id = " + slkjTicketCategory.getId());
            if (Objects.isNull(slkjTicketCategory.getId())) {
                log.info("slkj_ticket_category#id = null，不执行后续逻辑！");
                return;
            }

            Set<String> collect = Sets.newHashSet();
            List<SlkjFareSchemeDetail> schemeDetails = slkjFareSchemeDetailMapper.selectSlkjFareSchemeDetailList(null);
            if (CollectionUtils.isNotEmpty(schemeDetails)) {
                collect = schemeDetails.stream()
                        .map(schemeDetail -> schemeDetail.getFareSchemeId() + "_" + schemeDetail.getTicketCategoryId())
                        .collect(Collectors.toSet());
            }

            List<SlkjFareScheme> schemes = slkjFareSchemeMapper.selectSlkjFareSchemeList(null);
            if (CollectionUtils.isNotEmpty(schemes)) {
                List<SlkjTicketCategory> ticketCategories = slkjTicketCategoryMapper.selectSlkjTicketCategoryByLevelIds(new Long[]{slkjTicketCategory.getTicketLevelId()});
                for (SlkjFareScheme fareScheme : schemes) {
                    for (SlkjTicketCategory category : ticketCategories) {
                        if (collect.contains(fareScheme.getId() + "_" + category.getId())) {
                            log.info("票价方案：{}，票类：{}，已存在，跳过！", fareScheme.getId(), category.getId());
                            continue;
                        }
                        SlkjFareSchemeDetail data = getSlkjFareSchemeDetail(category, fareScheme.getId());
                        fareSchemeDetailList.add(data);
                    }
                }
            }

            if (CollectionUtils.isNotEmpty(fareSchemeDetailList)) {
                log.info("同步票类至票价方案详情：{}", JSON.toJSONString(fareSchemeDetailList));
                slkjFareSchemeDetailMapper.batchInsertSlkjFareSchemeDetail(fareSchemeDetailList);
            } else {
                log.info("暂无票价方案！");
            }
        } catch (Exception e) {
            log.error("处理票类同步逻辑时 [{}] 发生异常，触发数据回滚: {}", JSON.toJSONString(slkjTicketCategory), e.getMessage(), e);
            dingTalkService.sendTextMessage("同步票类至票价方案详情，发生异常，触发数据回滚：" + JSON.toJSONString(fareSchemeDetailList) + ",--->" + e.getMessage(), true);
            throw e;
        }
    }

    @Transactional
    public void handleOrder(SlkjOrder order) {
        try {
            Stopwatch stopwatch = Stopwatch.createStarted();
            log.info("事件 ID：{}", "slkj_order#order_id = " + order.getOrderId());
            if (Objects.isNull(order.getOrderId())) {
                log.info("slkj_order#order_id = null，不执行后续逻辑！");
                return;
            }
            if (order.getOrderType() == OrderType.OT_102.getCode()) {
                log.info("订单ID [{}] 订单类型为商品，无需分账", order.getOrderId());
                return;
            }

            AtomicInteger totalAmount = new AtomicInteger(order.getTotalAmount());

            List<SlkjOrderPricingSnapshot> pricingSnapshots = slkjOrderPricingSnapshotMapper.selectByOrderId(order.getOrderId());
            if (order.getOrderType() == OrderType.OT_103.getCode()) {
                log.info("订单ID [{}] 订单类型为商品+门票，商品不参与分账，需要将商品金额扣除", order.getOrderId());
                int sum = pricingSnapshots.stream()
                        .filter(i -> "GOODS".equals(i.getItemType()))
                        .mapToInt(SlkjOrderPricingSnapshot::getUserActualAmount)
                        .sum();
                totalAmount.addAndGet(-sum);
                log.info("订单ID [{}] 商品金额：{}，扣除商品金额后的总金额: {}", order.getOrderId(), sum, totalAmount.get());
            }

            // 这个逻辑至关重要
            if (totalAmount.get() == 0) {
                log.info("订单ID [{}] 订单金额为0，无需分账", order.getOrderId());

                // 初始化手环信息
                processOrderTracking(order, pricingSnapshots);

                log.info("订单 [{}] 分账逻辑耗时：{}", order.getOrderId(), stopwatch.stop());
                return;
            }

            List<SlkjOrderSplitSnapshot> splitSnapshots = slkjOrderSplitSnapshotMapper.selectByOrderId(order.getOrderId());
            log.info("订单ID [{}] 对应分账快照信息：{}", order.getOrderId(), JSON.toJSONString(splitSnapshots));
            if (CollectionUtils.isEmpty(splitSnapshots)) {
                log.info("订单ID [{}] 对应分账快照为空，无需分账", order.getOrderId());
                return;
            }

            List<String> relTypes = splitSnapshots.stream().map(SlkjOrderSplitSnapshot::getRelatedType).collect(Collectors.toList());
            Map<Long, SharesInvestorVo> investorMap;
            if (relTypes.contains(SlkjOrderSplitSnapshot.INVESTOR)) {
                investorMap = slkjShopInvestorMapper.selectInvestorShopByShopId(order.getShopId())
                        .stream()
                        .collect(Collectors.toMap(SharesInvestorVo::getId, Function.identity()));
            } else {
                investorMap = Collections.emptyMap();
            }

            List<SlkjOrderSplitSnapshot> updateSnapshots = new ArrayList<>();
            List<SlkjShopInvestor> shopInvestors = new ArrayList<>();

            AtomicReference<BigDecimal> currentShareRatio = new AtomicReference<>(BigDecimal.ZERO);
            log.info("计算投资方分账金额...");
            splitSnapshots.stream()
                    .filter(s -> Objects.equals(s.getRelatedType(), SlkjOrderSplitSnapshot.INVESTOR))
                    .filter(s -> investorMap.get(s.getRelatedId()) != null)
                    .forEach(splitSnapshot -> {
                        Long relatedId = splitSnapshot.getRelatedId();
                        // INVESTOR=投资方、PROJECT=项目、TICKET_SYSTEM=票务、OPERATION=运营
                        SharesInvestorVo investor = investorMap.get(relatedId);
                        AtomicReference<BigDecimal> ratio = new AtomicReference<>(BigDecimal.ZERO);

                        // 更新是否回本，以及回本总额
                        SlkjShopInvestor shopInvestor = new SlkjShopInvestor();
                        shopInvestor.setId(relatedId);
                        shopInvestor.setIsPrincipal("0");
                        AtomicBoolean flag = new AtomicBoolean(true);

                        if (investor.getAmount() == null || investor.getAmount() <= 0L) {
                            // 投资金额为 0，但是有分账比例，也要参与分账，使用回本后比例，如果回本后比例为空，或者 0 ，则分到 0 元
                            shopInvestor.setIsPrincipal("1");
                            if (splitSnapshot.getPostRecRatio() != null && splitSnapshot.getPostRecRatio().compareTo(BigDecimal.ZERO) > 0) {
                                ratio.set(splitSnapshot.getPostRecRatio());
                                splitSnapshot.setCurrentUseRatio(splitSnapshot.getPostRecRatio());
                            } else {
                                flag.set(false);
                                splitSnapshot.setCurrentUseRatio(BigDecimal.ZERO);
                                shopInvestor.setRedemptionAmount(0L);
                            }
                        } else {
                            // 已回本
                            if (investor.getIsPrincipal() != null && "1".equals(investor.getIsPrincipal())) {
                                if (splitSnapshot.getPostRecRatio() != null && splitSnapshot.getPostRecRatio().compareTo(BigDecimal.ZERO) > 0) {
                                    ratio.set(splitSnapshot.getPostRecRatio());
                                    splitSnapshot.setCurrentUseRatio(splitSnapshot.getPostRecRatio());
                                }
                            } else if (splitSnapshot.getPreRecRatio() != null && splitSnapshot.getPreRecRatio().compareTo(BigDecimal.ZERO) > 0) {
                                ratio.set(splitSnapshot.getPreRecRatio());
                                splitSnapshot.setCurrentUseRatio(splitSnapshot.getPreRecRatio());
                            }
                        }

                        // 当前这笔订单分账金额 单位：毫
                        if (ratio.get().compareTo(BigDecimal.ZERO) <= 0) {
                            splitSnapshot.setProfitShareAmount(0L);
                        } else {
                            long value = MoneyUtils.calculateShareAmountInMilliUnits(totalAmount.get(), ratio.get().doubleValue());
                            splitSnapshot.setProfitShareAmount(value);
                        }

                        splitSnapshot.setUpdateTime(DateUtils.getNowDate());
                        updateSnapshots.add(splitSnapshot);
                        currentShareRatio.set(currentShareRatio.get().add(ratio.get()));

                        if (flag.get()) {
                            long amount = investor.getRedemptionAmount() == null || investor.getRedemptionAmount() <= 0L ? 0L : shopInvestor.getRedemptionAmount();
                            shopInvestor.setRedemptionAmount(amount + splitSnapshot.getProfitShareAmount());
                            if ((investor.getAmount() * 100) <= shopInvestor.getRedemptionAmount()) {
                                // 已回本
                                shopInvestor.setIsPrincipal("1");
                            }
                        }
                        shopInvestors.add(shopInvestor);
                    });
            log.info("投资方当前分账比例：{}", currentShareRatio.get());

            log.info("计算 项目/票务，分账金额...");
            Set<String> validTypes = Sets.newHashSet(SlkjOrderSplitSnapshot.PROJECT, SlkjOrderSplitSnapshot.TICKET_SYSTEM);
            splitSnapshots.stream()
                    .filter(s -> validTypes.contains(s.getRelatedType()))
                    .forEach(splitSnapshot -> {
                        if (Objects.isNull(splitSnapshot.getSplitRatio()) || splitSnapshot.getSplitRatio().compareTo(BigDecimal.ZERO) <= 0) {
                            splitSnapshot.setProfitShareAmount(0L);
                            splitSnapshot.setCurrentUseRatio(BigDecimal.ZERO);
                        } else {
                            splitSnapshot.setProfitShareAmount(MoneyUtils.calculateShareAmountInMilliUnits(totalAmount.get(), splitSnapshot.getSplitRatio().doubleValue()));
                            splitSnapshot.setCurrentUseRatio(splitSnapshot.getSplitRatio());
                            currentShareRatio.set(currentShareRatio.get().add(splitSnapshot.getSplitRatio()));
                        }
                        splitSnapshot.setUpdateTime(DateUtils.getNowDate());
                        updateSnapshots.add(splitSnapshot);
                    });
            log.info("投资方+项目+票务系统，当前分账比例：{}", currentShareRatio.get());

            log.info("计算运营分账金额...");
            splitSnapshots.stream()
                    .filter(s -> Objects.equals(s.getRelatedType(), SlkjOrderSplitSnapshot.OPERATION))
                    .forEach(splitSnapshot -> {
                        BigDecimal operationRatio = BigDecimal.valueOf(100).subtract(currentShareRatio.get());
                        log.info("运营当前分账比例：{}", operationRatio);
                        splitSnapshot.setProfitShareAmount(MoneyUtils.calculateShareAmountInMilliUnits(totalAmount.get(), operationRatio.doubleValue()));
                        splitSnapshot.setCurrentUseRatio(operationRatio);
                        splitSnapshot.setUpdateTime(DateUtils.getNowDate());
                        updateSnapshots.add(splitSnapshot);

                        SlkjShopOperate shopOperate = new SlkjShopOperate();
                        shopOperate.setCurrentShareRatio(operationRatio);
                        shopOperate.setId(splitSnapshot.getRelatedId());
                        log.info("更新运营当前分账比例：{}", JSON.toJSONString(shopOperate));
                        slkjShopOperateMapper.updateSlkjShopOperate(shopOperate);
                    });

            log.info("批量更新分账金额：{}", JSON.toJSONString(updateSnapshots));
            slkjOrderSplitSnapshotMapper.batchUpdateProfitShareAmount(updateSnapshots);

            if (CollectionUtils.isNotEmpty(shopInvestors)) {
                log.info("批量更新投资方回本总额：{}", JSON.toJSONString(shopInvestors));
                slkjShopInvestorMapper.batchUpdateRedemptionAmount(shopInvestors);
            }

            log.info("更新订单 [{}] ticket_total_amount={}", order.getOrderId(), totalAmount.get());
            slkjOrderMapper.updateTicketTotalAmount(order.getOrderId(), totalAmount.get());

            // 初始化手环信息
            processOrderTracking(order, pricingSnapshots);

            log.info("订单 [{}] 分账逻辑耗时：{}", order.getOrderId(), stopwatch.stop());
        } catch (Exception e) {
            log.error("处理订单 [{}] 分账异常，触发数据回滚：{}", JSON.toJSONString(order), e.getMessage());
            dingTalkService.sendTextMessage("订单分账异常，触发回滚：" + JSON.toJSONString(order) + ",--->" + e.getMessage(), true);
            throw e;
        }
    }

    private void processOrderTracking(SlkjOrder order, List<SlkjOrderPricingSnapshot> pricingSnapshots) {
        // 统计总票数
        int includedTicketQuantity = pricingSnapshots.stream()
                .filter(i -> "TICKET".equals(i.getItemType()))
                .mapToInt(snap -> snap.getQuantity() * snap.getBayQuantity())
                .sum();

        // 构建手环信息
        SlkjOrderTracking orderTracking = buildOrderTracking(order, includedTicketQuantity);

        log.info("插入订单 [{}] 对应打印领取信息：{}", order.getOrderId(), JSON.toJSONString(orderTracking));
        slkjOrderTrackingMapper.insert(orderTracking);
    }

    private static SlkjOrderTracking buildOrderTracking(SlkjOrder order, int includedTicketQuantity) {
        SlkjOrderTracking orderTracking = new SlkjOrderTracking();
        orderTracking.setOrderId(order.getOrderId());
        orderTracking.setOrderNumber(order.getOrderNumber());
        orderTracking.setIncludedTicketQuantity(includedTicketQuantity);
        orderTracking.setValidatedTicketQuantity(0);
        orderTracking.setPrintedWristbandQuantity(0);
        orderTracking.setReceivedDeviceQuantity(0);
        return orderTracking;
    }

    private SlkjFareSchemeDetail getSlkjFareSchemeDetail(SlkjTicketCategory slkjTicketCategory, Long fareSchemeId) {
        SlkjFareSchemeDetail data = new SlkjFareSchemeDetail();
        data.setFareSchemeId(fareSchemeId);
        data.setTicketLevelId(slkjTicketCategory.getTicketLevelId());
        data.setTicketCategoryId(slkjTicketCategory.getId());
        data.setShopPrice(null);
        data.setMiniProgramPrice(null);
        data.setDouyinPrice(null);
        data.setOriginalPrice(null);
        data.setStatus(0);
        data.setChecked((byte) 0);
        data.setCreateTime(DateUtils.getNowDate());
        return data;
    }

    @Transactional
    public void handleRefundOrder(RefundOrderDto data) {
        try {
            log.info("处理退款订单 [{}]，分账回退：{}", data.getOrderId(), JSON.toJSONString(data));
            if (Objects.isNull(data.getOrderId())) {
                log.info("订单ID为空，不处理后续逻辑！");
                return;
            }
            if (data.getOrderType() == OrderType.OT_102.getCode()) {
                log.info("当前订单属于商品订单，无需处理！");
                return;
            }

            if (data.getTotalAmount() == 0) {
                log.info("当前订单金额 [{}] 为0，无需处理！", data.getOrderId());
                return;
            }

            List<SlkjOrderSplitSnapshot> splitSnapshots = slkjOrderSplitSnapshotMapper.selectByOrderId(data.getOrderId());
            if (CollectionUtils.isNotEmpty(splitSnapshots)) {
                List<SharesInvestorVo> shopInvestors = slkjShopInvestorMapper.selectInvestorShopByShopId(data.getShopId());
                Map<Long, SharesInvestorVo> collect = Collections.emptyMap();
                if (CollectionUtils.isNotEmpty(shopInvestors)) {
                    collect = shopInvestors.stream().collect(Collectors.toMap(SharesInvestorVo::getId, Function.identity()));
                }

                AtomicReference<BigDecimal> currentShareRatio = new AtomicReference<>(BigDecimal.ZERO);
                List<SlkjShopInvestor> sis = new ArrayList<>();
                List<SlkjOrderSplitSnapshot> updateSnapshots = new ArrayList<>();
                SlkjOrderSplitSnapshot snapshot;
                SlkjShopInvestor shopInvestor;
                Long operateId = null;
                for (SlkjOrderSplitSnapshot splitSnapshot : splitSnapshots) {
                    if (splitSnapshot.getRelatedType().equals(SlkjOrderSplitSnapshot.INVESTOR)) {
                        shopInvestor = new SlkjShopInvestor();
                        shopInvestor.setId(splitSnapshot.getRelatedId());
                        shopInvestor.setIsPrincipal("0");
                        SharesInvestorVo investor = collect.get(splitSnapshot.getRelatedId());
                        if (Objects.isNull(investor)) {
                            continue;
                        }

                        shopInvestor.setRedemptionAmount(investor.getRedemptionAmount() - (splitSnapshot.getProfitShareAmount() == null ? 0L : splitSnapshot.getProfitShareAmount()));
                        long l = investor.getAmount() == null || investor.getAmount() <= 0 ? 0L : investor.getAmount();
                        if ((l * 100) <= shopInvestor.getRedemptionAmount()) {
                            // 已回本
                            shopInvestor.setIsPrincipal("1");
                            currentShareRatio.set(splitSnapshot.getPostRecRatio());
                        } else {
                            currentShareRatio.set(splitSnapshot.getPreRecRatio());
                        }
                        sis.add(shopInvestor);
                    } else if (splitSnapshot.getRelatedType().equals(SlkjOrderSplitSnapshot.PROJECT) || splitSnapshot.getRelatedType().equals(SlkjOrderSplitSnapshot.TICKET_SYSTEM)) {
                        if (splitSnapshot.getSplitRatio() != null && splitSnapshot.getSplitRatio().compareTo(BigDecimal.ZERO) > 0) {
                            currentShareRatio.set(currentShareRatio.get().add(splitSnapshot.getSplitRatio()));
                        }
                    } else if (splitSnapshot.getRelatedType().equals(SlkjOrderSplitSnapshot.OPERATION)) {
                        operateId = splitSnapshot.getRelatedId();
                    }

                    snapshot = new SlkjOrderSplitSnapshot();
                    snapshot.setId(splitSnapshot.getId());
                    snapshot.setProfitShareAmount(0L);
                    snapshot.setCurrentUseRatio(splitSnapshot.getCurrentUseRatio());
                    snapshot.setUpdateTime(DateUtils.getNowDate());
                    updateSnapshots.add(snapshot);
                }

                if (operateId != null) {
                    SlkjShopOperate shopOperate = new SlkjShopOperate();
                    BigDecimal operationRatio = BigDecimal.valueOf(100).subtract(currentShareRatio.get());
                    shopOperate.setCurrentShareRatio(operationRatio);
                    shopOperate.setId(operateId);
                    log.info("更新运营当前分账比例：{}", JSON.toJSONString(shopOperate));
                    slkjShopOperateMapper.updateSlkjShopOperate(shopOperate);
                }

                log.info("批量更新投资方回本总额：{}", JSON.toJSONString(sis));
                if (CollectionUtils.isNotEmpty(sis)) {
                    slkjShopInvestorMapper.batchUpdateRedemptionAmount(sis);
                }

                log.info("批量更新订单 [{}] 分账记录：{}", data.getOrderId(), JSON.toJSONString(updateSnapshots));
                slkjOrderSplitSnapshotMapper.batchUpdateProfitShareAmount(updateSnapshots);
            }
        } catch (Exception e) {
            log.error("处理退款订单分账回退 [{}] 异常，触发数据回滚：{}", JSON.toJSONString(data), e.getMessage());
            dingTalkService.sendTextMessage("退款订单分账回退异常，触发数据回滚：" + JSON.toJSONString(data) + ",--->" + e.getMessage(), true);
            throw e;
        }
    }

    @Transactional
    public void handleCreateFareScheme(Long fareSchemeId) {
        try {
            if (Objects.isNull(fareSchemeId)) {
                log.info("票价方案ID为空，不处理后续逻辑！");
                return;
            }
            List<Long> ids = slkjFareSchemeDetailMapper.selectSlkjFareSchemeDetailByFareSchemeIds(Collections.singletonList(fareSchemeId))
                    .stream()
                    .map(SlkjFareSchemeDetail::getTicketCategoryId)
                    .collect(Collectors.toList());

            List<SlkjTicketCategory> ticketCategories = slkjTicketCategoryMapper.selectSlkjTicketCategoryList(null);
            if (ids.size() == ticketCategories.size()) {
                log.info("当前方案 [{}] 已包含所有票类，无需新增！", fareSchemeId);
                return;
            }

            List<SlkjFareSchemeDetail> fareSchemeDetails = new ArrayList<>();
            ticketCategories.stream()
                    .filter(ticketCategory -> !ids.contains(ticketCategory.getId()))
                    .forEach(ticketCategory -> {
                        SlkjFareSchemeDetail schemeDetail = getSlkjFareSchemeDetail(ticketCategory, fareSchemeId);
                        fareSchemeDetails.add(schemeDetail);
                    });
            log.info("新增方案 [{}] 票类：{}", fareSchemeId, JSON.toJSONString(fareSchemeDetails));
            slkjFareSchemeDetailMapper.batchInsertSlkjFareSchemeDetail(fareSchemeDetails);
        } catch (Exception e) {
            log.error("处理fareScheme [{}] 异常，触发数据回滚：{}", fareSchemeId, e.getMessage());
            dingTalkService.sendTextMessage("新增票价方案，同步票类出现异常，触发回滚：fareSchemeId=" + fareSchemeId + ",--->" + e.getMessage(), true);
            throw e;
        }
    }

}
